from pyproj import Proj;
from xml.dom.minidom import Document
import string;
import subprocess;
import math;
from TrafficSimulator.MapInterface.Simulation.sumolib import net

class SimConfig:

	def __init__(self, formData):
		#self.initLog();
		self._dataProcessor = DataProcessor(formData);
		
		#self._dataProcessor.DownloadMap();	
		self._NetFile = self._dataProcessor.ConvertMap();
		# Check if none if not output error message
		self._Network = net.readNet(self._NetFile);
		networkNodes = self._Network.getNodes();
		networkEdges = self._Network.getEdges();
		self._pointFactory = PointFactory(self._Network.getProjParameter(),self._Network.getNetOffset());
		
		self._flowFactory = FlowFactory(self._pointFactory);
		self._dataProcessor.makeFlowCfgs(self._flowFactory);
		self._flowComputer = FlowComputer(self._flowFactory,networkNodes,networkEdges);
		#edgeSrc = self._Network.getEdge(self._flowComputer.getFlows()).getFrom().getCoord();
		#edgeDest = self._Network.getEdge(self._flowComputer.getFlows()).getTo().getCoord();
		#srcOpen = self._pointFactory.newPoint('sumo', edgeSrc);
		#destOpen = self._pointFactory.newPoint('sumo', edgeDest);
		#print srcOpen.getOpen();
		#print destOpen.getOpen();
		
class FlowComputer(object):
	
	def __init__(self,flowFactory,networkNodes, networkEdges):
		
		self._flowFactory = flowFactory;
		self._networkNodes = list(networkNodes);
		self._networkEdges = list(networkEdges);
		self._flowCfgs = flowFactory.getFlowCfgs();
		self._sourceEdges=[];
		self._destEdges=[];
		self._currentFlowCfg =0;
		for i in range(len(self._flowCfgs)):
			self._currentFlowCfg = i;
			sourcePoint = self._flowCfgs[i].getSource().getSumo();
			fHopPoint = self._flowCfgs[i].getFirstHop().getSumo();
			lHopPoint =self._flowCfgs[i].getLastHop().getSumo();
			destPoint = self._flowCfgs[i].getDest().getSumo();
			srcEdge = self.computeEdge(sourcePoint, fHopPoint);
			#destEdge = self.computeEdge(lHopPoint, destPoint);					#################
			rate = self._flowCfgs[i].getRate();
			startTime = self._flowCfgs[i].getStartTime();
			endTime = self._flowCfgs[i].getEndTime();
			self._sourceEdges.append(srcEdge);
			#self._destEdges.append(destEdge);									#################
			#self._createdFlows = flowFactory.newFlow(srcEdge,destEdge,rate,startTime,endTime);		###################
		
	def getFlows(self):
		return self._sourceEdges[0];
		
		
	def sortCoordSource(self,node):
		coords = node.getCoord();
		netX = coords[0];
		netY = coords[1];
		sourcePoint = self._flowCfgs[self._currentFlowCfg].getSource().getSumo();
		currentDist = math.sqrt(((netX-sourcePoint[0])**2)+((netY-sourcePoint[1])**2));
		node.dist = currentDist;
		return currentDist;	

	def sortCoordFirst(self,node):
		coords = node.getCoord();
		netX = coords[0];
		netY = coords[1];
		firstHop = self._flowCfgs[self._currentFlowCfg].getFirstHop().getSumo();
		currentDist = math.sqrt(((netX-firstHop[0])**2)+((netY-firstHop[1])**2));
		node.dist = currentDist;
		return currentDist;
	
	def sortEq(self,Eq) :
		a=Eq['a'];
		b=Eq['b'];
		c=Eq['c'];
		sourcePoint = self._flowCfgs[self._currentFlowCfg].getSource().getSumo();
		dist = math.fabs( a*sourcePoint[0] + b*sourcePoint[0] + c)/math.sqrt(a**2 + b**2);
		Eq['dist'] = dist;
		return dist;
	
	def getIds(self,edgeList):
		edgeIds =[];
		for edge in edgeList:
			id = edge.getId().split('#')[0];
			edgeIds.append(id);
		return edgeIds;
	


	
	def computeEdge(self,sourcePoint,firstHop):
		empX = sourcePoint[0];
		empY = sourcePoint[1];
		firstHopX = firstHop[0];
		firstHopY = firstHop[1];
		print "flowSource = ";
		print sourcePoint;
		print "flowFirstHop = "; 
		print firstHop;
		NodesBySource= list(self._networkNodes);
		NodesByFirst = list(NodesBySource); #Construct a new list don't do shallow copying
		NodesBySource.sort(key=self.sortCoordSource);
		NodesByFirst.sort(key=self.sortCoordFirst);
		SNodes = NodesBySource[0:3];						#Candidate Source Nodes
		FNodes = NodesByFirst[0:3];							#Candidate First Hop Nodes
		candidateNodeIds = [];
		Edges = [];
		# Get All edges connected to nearby nodes
		for node in SNodes:
			#print sumoToOpen(node.getCoord());
			candidateNodeIds.append(float(node.getId()));
			incEdges = node.getIncoming();
			outEdges = node.getOutgoing();
			Edges += incEdges + outEdges;		
			Edges += list(set(Edges)); # Remove duplicates if present
	
		for FNode in FNodes:
			FEdges = self.getIds(FNode.getIncoming());
			#print "{0},{1}".format(sumoToOpen(FNode.getCoord())['lat'],sumoToOpen(FNode.getCoord())['lon']);
			#print FEdges;
			for SNode in SNodes:
				#print "Source : ";
				#print "{0},{1}".format(sumoToOpen(SNode.getCoord())['lat'],sumoToOpen(SNode.getCoord())['lon']);
				SEdges = self.getIds(SNode.getOutgoing());
				#print SEdges;
				Common = list(set(FEdges) & set(SEdges));
				if (len(Common) == 1):
					print "{0},{1}".format(sumoToOpen(FNode.getCoord())['lat'],sumoToOpen(FNode.getCoord())['lon']);
					print "{0},{1}".format(sumoToOpen(SNode.getCoord())['lat'],sumoToOpen(SNode.getCoord())['lon']);
					return Common[0];

	#If edge isn't found based on previous method resort to this one.	
		
		candidateEdges = [];
		for edge in Edges:
			if ((float(edge.getTo().getId()) in candidateNodeIds) and (float(edge.getFrom().getId()) in candidateNodeIds) ):
				candidateEdges.append(edge);
			
		
		candidateEdges = list(set(candidateEdges));
		#print candidateEdges;
		
		
		candidateEdgeEqs = [];	
		for edge in candidateEdges:
			fromX = float(edge.getFrom().getCoord()[0]);
			fromY=float(edge.getFrom().getCoord()[1]);
			toX= float(edge.getTo().getCoord()[0]);
			toY = float(edge.getTo().getCoord()[1]);
			Eq = {
			'a': fromY-toY,
			'b': toX - fromX,
			'c': fromX*toY-toX*fromY,
			'id': edge.getId(),
			'from': edge.getFrom(),
			'to': edge.getTo()
			}
			candidateEdgeEqs.append(Eq);	
		candidateEdgeEqs.sort(key=self.sortEq);
		TheChosenEdge = candidateEdgeEqs[0]['id'];
		return TheChosenEdge;
	
	def computeDestEdge():
		a=1;
	
	def verifyFlow():
		a=1;


class PointFactory(object):
	def __init__(self, projParam, netOffset):
		self._projParam = projParam
		self._netOffset = netOffset;
	def newPoint(self, type, coord=None):
		return Point(self._projParam, self._netOffset, type, coord);
		
class Point(object):
	
	def __init__(self,projParam, netOffset,type,coord = None):
		self._projParam = ''.join(projParam);
		self._netOffset = float(netOffset[0]),float(netOffset[1]);	#Construct new copy of list
		self._x=0;
		self._y=0;
		self._lon=0;
		self._lat=0;
		if (coord != None):
			if (type == 'sumo'):
				self._x = float(coord[0]);
				self._y = float(coord[1]);
				self.setOpen(coord);
			if (type == 'open'):
				self._lat = float(coord[0]);
				self._lon = float(coord[1]);
				self.setSumo(coord);
	
	def setSumo(self,coord):	
		xOffset = self._netOffset[0];
		yOffset = self._netOffset[1];
		p = Proj(str(self._projParam));
		x,y = p(self._lon,self._lat);
		x = x + xOffset;
		y = y + yOffset;
		self._x = x;
		self._y = y;

	
	def setOpen(self, coord):
		xOffset = self._netOffset[0];
		yOffset = self._netOffset[1];
		p = Proj(str(self._projParam));
		x1 = self._x - xOffset;
		y1 = self._y - yOffset;
		self._lon,self._lat = p(x1,y1,inverse=True);
		
	def getOpen(self):
		coord = self._lat,self._lon;
		return coord;
		
	def getSumo(self):
		coord = self._x,self._y;
		return coord;



class FlowFactory(object):	#Keep track of created flows
	
	def __init__(self,pointFactory,defaultStart=None, defaultEnd=None):
		self._pointFactory = pointFactory;
		self._currentFlowCfgs=[];
		self._currentFlows=[];
		self._flowId=0;
		if (defaultStart == None):
			self._defaultStart = 0;
		else:
			self._defaultStart = defaultStart;
		if (defaultEnd == None):
			self._defaultEnd = 1000;
		else:
			self._defaultEnd = defaultEnd;
	
	def newFlowCfg(self, source, firstHop, lastHop, destination, rate, startTime=None, endTime=None):
		src= self._pointFactory.newPoint('open',source);
		dest= self._pointFactory.newPoint('open',destination);
		fHop = self._pointFactory.newPoint('open',firstHop);
		lHop = self._pointFactory.newPoint('open',lastHop);
		if (startTime == None):
			if (endTime == None):
				flow = FlowCfg(src, fHop, lHop, dest, rate,self._defaultStart, self._defaultEnd);
				self._currentFlowCfgs.append(flow);
				return flow;
			else :
				flow = FlowCfg(src, fHop, lHop,dest, rate,self._defaultStart,endTime);
				self._currentFlowCfgs.append(flow);
				return flow;
		
		if (endTime == None):
			if (startTime != None):
				flow = FlowCfg(src, fHop, lHop,dest, rate,startTime, self.defaultEnd);
				self._currentFlowCfgs.append(flow);
				return flow;
		
		
		flow = FlowCfg(src, fHop, lHop,dest,rate,startTime, endTime);
		self._currentFlowCfgs.append(flow);
		return flow;
	
	def newFlow(self,srcEdge, destEdge, rate, startTime=None, endTime=None):
		if (startTime == None):
			if (endTime == None):
				flow = Flow(self._flowId,srcEdge,destEdge,rate, self._defaultStart, self._defaultEnd);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
			else:
				flow = Flow(self._flowId,srcEdge,destEdge,rate, self._defaultStart,endTime);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
				
		if (endTime == None):
			if (startTime != None):
				flow = Flow(self._flowId,srcEdge,destEdge,rate, startTime,self._defaultEnd);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
				
		flow = Flow(self._flowId,srcEdge,destEdge,rate, startTime ,endTime);
		self._flowId = self._flowId +1;
		self._currentFlows.append(flow);
		return flow;
		
	def getFlowCfgs(self):
		return self._currentFlowCfgs;

class Flow(object):

	def __init__(self,id, srcEdge,destEdge,rate,start,end):
		self._id = id;
		self._sourceEdge  = srcEdge;
		self._destEdge = destEdge;
		self._rate = rate;
		self._startTime = start;
		self._endTime = end;

	def getStartTime():
		return self._startTime;
		
	def getEndTime():
		return self._endTime;
		
	def getId():
		return self._id;
	
	def getSourceEdge():
		return self._sourceEdge;
	
	def getDestEdge():
		return self._destEdge;
	
	def getRate():	
		return self._rate;


class FlowCfg(object):

	def __init__(self,src, fHop, lHop,dest,rate, start,end): 
		self._source = src;
		self._fHop = fHop;
		self._lHop = lHop;
		self._destination = dest;
		self._rate = rate;
		self._startTime = start;
		self._endTime = end;
		
	def getSource(self):
		return self._source;
	
	def getFirstHop(self):
		return self._lHop;
	
	def getLastHop(self):
		return self._lHop;
	
	def getDest(self):
		return self._destination;
	
	def getRate(self):
		return self._rate;
		
	def getStartTime(self):
		return self._startTime;
		
	def getEndTime(self):
		return self._endTime;

class DataProcessor(object):
	
	def __init__(self, formData):
		
		self._UserId = formData.get('UserId')[0];
		self._flowSourcePoints = self.readPoints(formData.get('flowSources')[0]);
		self._flowDestinationPoints = self.readPoints(formData.get('flowDestinations')[0]);
		
		
		self._flowFirstHopPoints = self.readPoints(formData.get('flowFirstHops')[0]);
		self._flowLastHopPoints =  self.readPoints(formData.get('flowLastHops')[0]);
		
		self._flowRates = self.readValues('float',formData.get('flowRates')[0]);
		self._osmExportLink,self._osmMapLink = self.writeLinks(formData.get('top')[0],formData.get('right')[0],formData.get('bottom')[0],formData.get('left')[0]);
		self._SimulationFolder = "Simulation_"+self._UserId;
		#print self._flowSourcePoints;
		#print self._osmMapLink;
		
		
	def writeLinks(self,top,right,bottom,left):
		
		osmBoundaries = left+","+bottom+","+right+","+top;
		osmExportLink = "http://api.openstreetmap.org/api/0.6/map?bbox="+osmBoundaries;
		osmMapLink = "http://www.openstreetmap.org/?box=yes&maxlat="+top+"&maxlon="+right+"&minlat="+bottom+"&minlon="+left;
		return osmExportLink,osmMapLink;
		
	def readPoints(self,data):
		flowSources = string.replace(data,'(','');
		flowSources = string.replace(flowSources,')','');
		flowSourcePoints = flowSources.split('s')
		flowSourcePointsArray = [];
		for p in flowSourcePoints:
			pointFormat = p.split(',')
			flowSourcePointsArray.append(pointFormat);
		return flowSourcePointsArray;
		
	def readValues(self,outputType,data):
		vals = data.split('s');
		outputVals = [];
		if (outputType == 'float'):
			for a in vals:
				outputVals.append(float(a)); 
			return outputVals;
		return vals;

	def makeFlowCfgs(self,flowCfgFactory):
		
		for i in range(len(self._flowSourcePoints)):
			src= self._flowSourcePoints[i];
			fhop= self._flowFirstHopPoints[i];
			lhop=self._flowLastHopPoints[i];
			dest= self._flowDestinationPoints[i];
			rate= self._flowRates[i];
			flowCfgFactory.newFlowCfg(src, fhop, lhop, dest, rate);

	
	def DownloadMap(self):
		makeFolderCmd = "mkdir "+self._SimulationFolder;
		subprocess.call(makeFolderCmd, shell = True);
		downloadMapCmd = "wget "+self._osmExportLink+ " -O "+ self._SimulationFolder + "/map.osm";
		subprocess.call(downloadMapCmd, shell = True);
	
	def ConvertMap(self):
		makeFolderCmd = "mkdir "+self._SimulationFolder;
		subprocess.call(makeFolderCmd, shell = True);
		convertMapCmd = "/opt/local/bin/netconvert --osm-files "+self._SimulationFolder+"/map.osm --proj.utm -o "+self._SimulationFolder+"/netMap.net.xml";
		if (subprocess.call(convertMapCmd, shell = True) == 0):
			print "Map Converted successfully";
			print self._SimulationFolder+"/netMap.net.xml"
			return self._SimulationFolder+"/netMap.net.xml";
		return None;

class XMLWriter:
	
	def __init__(self):
		self._flowFile="";
		
	def writeFlowDef(self, fileName, flowList):
		self._flowFile = fileName;
		doc = Document();
		rootTag = doc.createElement('flowdefs');
		doc.appendChild(rootTag);
		intervalTag= doc.createElement('interval');
	
		for i in range(len(flowList)): 
			intervalTag.setAttribute('begin',flowList[i].getStartTime());
			intervalTag.setAttribute('end',flowList[i].getEndTime());
			flowTag = doc.createElement('flow');
			flowTag.setAttribute('id',flowList[i].getId());
			flowTag.setAttribute('from',flowList[i].getSourceEdge());
			flowTag.setAttribute('to',flowList[i].getDestEdge());
			flowTag.setAttribute('number',(float(flowList[i].getEndTime())-float(flowList[i].getStartTime()))/float(flowList[i].getRate()));
			intervalTag.appendChild(flowTag);
		
		f= open(fileName,'w');
		f.write(doc.toxml());
		
		return filename;
	
	
		
		
xOffset = -728301.16
yOffset = -3750839.32
projParam = "+proj=utm +zone=36 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
netOffset = xOffset,yOffset;
pointFactory = PointFactory(projParam,netOffset);
coord = 33.899884,35.47468;
myPoint = pointFactory.newPoint('open',coord);
#print myPoint.getSumo();
#print myPoint.getOpen();


